import Docxtemplater from 'docxtemplater';
import PizZip from 'pizzip';
import PizZipUtils from 'pizzip/utils/index.js';
import ImageModule from 'docxtemplater-image-module-free';
import expressionParser from 'docxtemplater/expressions';

type integer = number;

interface Part {
  type: string;
  value: string;
  module: string;
  raw: string;
  offset: integer;
  lIndex: integer;
  num: integer;
  inverted?: boolean;
  endLindex?: integer;
  expanded?: Part[];
  subparsed?: Part[];
  position?: string;
  tag?: string;
}

interface ParserContext {
  meta: {
    part: Part;
  };
  scopeList: any[];
  scopePath: string[];
  scopePathItem: integer[];
  scopePathLength: integer[];
  num: integer;
}

interface Parser {
  get(scope: any, context: ParserContext): any;
}

interface Syntax {
  allowUnopenedTag?: boolean;
  allowUnclosedTag?: boolean;
  changeDelimiterPrefix?: string | null;
}

interface IOption {
  nullGetter?(part: Part): any;
  linebreaks?: boolean;
  paragraphLoop?: boolean;
  delimiters?: { start: string; end: string };
  parser?(tag: string): Parser;
  errorLogging?: boolean | string;
  syntax?: Syntax;
  stripInvalidXMLChars?: boolean;

  fileName?: string;
  imgSize?: number[] | string;
  imgMaxSize?: number[];
  imgMinSize?: number[];
  parserConfig?: Record<string, any>;
}

interface IConfig {
  url: string;
  options?: IOption | null;
}

type Idata = Record<string, any>;

interface IImageOption {
  centered: boolean;
  getImage: Function;
  getSize: Function;
}

class Export2Word {
  private url: IConfig['url'] = '';
  private options: IConfig['options'] = null;
  private zip: PizZip | null = null;
  private imageOptions: IImageOption = {
    centered: false,
    getImage: (chartId: string) => this.base64DataURLToArrayBuffer(chartId),
    getSize: (img: any, tagValue: any, tagName: string) => {},
  };
  constructor(params: IConfig) {
    this.url = params.url;
    const defaultOps = {
      nullGetter: () => '',
      linebreaks: true,
      paragraphLoop: true,
      delimiters: { start: '{{', end: '}}' },
      imgSize: 'auto',
      imgMaxSize: [794, 476],
      imgMinSize: [600, 360],
      fileName: 'output.docx',
    };
    if (params.options) {
      this.options = { ...defaultOps, ...params.options! };
      this.options.parser = expressionParser.configure(
        params.options.parserConfig ? params.options.parserConfig : {}
      );
    } else {
      this.options = defaultOps;
    }

    this.imageOptions.getSize = (img: any, tagValue: any, tagName: string) => {
      const _this = this;
      return new Promise(function (resolve, reject) {
        const image = new Image();
        let w = 600,
          h = 240;
        image.src = tagValue;
        image.onload = function () {
          // 判断图片的尺寸是否是自动
          if (_this.options?.imgSize && typeof _this.options?.imgSize === 'string') {
            // 判断图片的尺寸是否满足尺寸要求
            if (_this.options?.imgMaxSize && image.width > _this.options?.imgMaxSize[0]) {
              w = _this.options.imgMaxSize[0];
            } else if (_this.options?.imgMinSize && image.width < _this.options.imgMinSize[0]) {
              w = _this.options.imgMinSize[0];
            } else {
              w = image.width;
            }
            if (_this.options?.imgMaxSize && image.height > _this.options?.imgMaxSize[1]) {
              h = _this.options.imgMaxSize[1];
            } else if (_this.options?.imgMinSize && image.height < _this.options.imgMinSize[1]) {
              h = _this.options.imgMinSize[1];
            } else {
              h = image.height;
            }
          } else if (_this.options?.imgSize && Array.isArray(_this.options.imgSize)) {
            // 判断是否是固定尺寸
            w = _this.options.imgSize[0];
            h = _this.options.imgSize[1];
          }
          resolve([w, h]);
        };
        image.onerror = function (e) {
          reject([w, h]);
        };
      });
    };
  }

  /**
   * 根据文件流下载文件
   * @param blob 文件流
   * @param fileName
   */
  private downloadFile = (blob: Blob, fileName: string): void => {
    const url = URL.createObjectURL(blob);
    // 通过a标签下载文件
    const a = document.createElement('a');
    a.href = url;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  /**
   * base64图片转换为二进制工具函数
   * @param dataURL base64地址
   * @returns 二进制文件流
   */
  private base64DataURLToArrayBuffer = (dataURL: string): ArrayBuffer => {
    const base64 = dataURL.split(',')[1];
    const binaryString = atob(base64);
    const len = binaryString.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes.buffer;
  };

  /**
   * 根据传递的参数导出word文档
   * @param data 需要填充的数据
   * @returns void
   */
  exportToWord(data: Idata): void {
    PizZipUtils.getBinaryContent(this.url, (error: any, content: any) => {
      if (error) {
        throw error;
      }

      this.zip = new PizZip(content);
      const doc = new Docxtemplater(this.zip, {
        modules: [new ImageModule(this.imageOptions)],
        nullGetter: this.options?.nullGetter,
        linebreaks: this.options?.linebreaks,
        paragraphLoop: this.options?.paragraphLoop,
        delimiters: this.options?.delimiters,
        parser: this.options?.parser,
        errorLogging: this.options?.errorLogging,
        syntax: this.options?.syntax,
        stripInvalidXMLChars: this.options?.stripInvalidXMLChars,
      });

      try {
        doc.renderAsync(data).then(() => {
          const out = doc.getZip().generate({
            type: 'blob',
            mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
          });
          // saveAs(out, `${data.courseClassName}-${data.courseSubject}课分析报告.docx`);
          this.downloadFile(out, this.options?.fileName as string);
        });
      } catch (error) {
        console.error(JSON.stringify({ error }));
        throw error;
      }
    });
  }
}

export default Export2Word;
